
#include "trap.h"
#include "../inc/x86.h"
#include "../mm/mmu.h"
#include "../inc/stdio.h"
#include "../driver/console.h"
#include "../libs/string.h"
#define DPL_KERNEL    (0)
#define DPL_USER    (3)

#define GD_KTEXT    (1 << 3)        // kernel text

#define USER_CS        ((((3) << 3)) | (3))
#define USER_DS        ((((4) << 3)) | (3))

#define KERNEL_CS    ((((1) << 3)) | (0))
#define KERNEL_DS    ((((2) << 3)) | (0))


volatile size_t ticks;

#define TICK_NUM 1000 

static const char *IA32flags[] = {
    "CF", NULL, "PF", NULL, "AF", NULL, "ZF", "SF",
    "TF", "IF", "DF", "OF", NULL, NULL, "NT", NULL,
    "RF", "VM", "AC", "VIF", "VIP", "ID", NULL, NULL,
};
static const char *
trapname(int trapno) {
    static const char * const excnames[] = {
        "Divide error",
        "Debug",
        "Non-Maskable Interrupt",
        "Breakpoint",
        "Overflow",
        "BOUND Range Exceeded",
        "Invalid Opcode",
        "Device Not Available",
        "Double Fault",
        "Coprocessor Segment Overrun",
        "Invalid TSS",
        "Segment Not Present",
        "Stack Fault",
        "General Protection",
        "Page Fault",
        "(unknown trap)",
        "x87 FPU Floating-Point Error",
        "Alignment Check",
        "Machine-Check",
        "SIMD Floating-Point Exception"
    };

    if (trapno < sizeof(excnames)/sizeof(const char * const)) {
        return excnames[trapno];
    }
    if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16) {
        return "Hardware Interrupt";
    }
    return "(unknown trap)";
}
/* *
 * Interrupt descriptor table:
 *
 * Must be built at run time because shifted function addresses can't
 * be represented in relocation records.
 * */
static struct gatedesc idt[256] = {{0}};
static struct pseudodesc idt_pd = {
    sizeof(idt) - 1, (uint32_t)idt
};
/* idt_init - initialize IDT to each of the entry points in kern/trap/vectors.S */
void idt_init(void) {
     /* LAB1 YOUR CODE : STEP 2 */
     /* (1) Where are the entry addrs of each Interrupt Service Routine (ISR)?
      *     All ISR's entry addrs are stored in __vectors. where is uintptr_t __vectors[] ?
      *     __vectors[] is in kern/trap/vector.S which is produced by tools/vector.c
      *     (try "make" command in lab1, then you will find vector.S in kern/trap DIR)
      *     You can use  "extern uintptr_t __vectors[];" to define this extern variable which will be used later.
      * (2) Now you should setup the entries of ISR in Interrupt Description Table (IDT).
      *     Can you see idt[256] in this file? Yes, it's IDT! you can use SETGATE macro to setup each item of IDT
      * (3) After setup the contents of IDT, you will let CPU know where is the IDT by using 'lidt' instruction.
      *     You don't know the meaning of this instruction? just google it! and check the libs/x86.h to know more.
      *     Notice: the argument of lidt is idt_pd. try to find it!
      */
    extern uint32_t __vectors[];
    int i;
    // 首先通过tools/vector.c通过程序生成/kern/trap/verctor.S,并在加载内核时对之前已经声明的全局变量__vectors进行整体的赋值
    // __vectors数组中的每一项对应于中断描述符的中断服务例程的入口地址，在SETGATE宏的使用中可以体现出来
    // 将__vectors数组中每一项关于中断描述符的描述设置到下标相同的idt中，通过宏SETGATE构造出最终的中断描述符结构
    for (i = 0; i < sizeof(idt) / sizeof(struct gatedesc); i ++) {
    	// 遍历idt数组，将其中的内容(中断描述符)设置进IDT中断描述符表中(默认的DPL特权级都是内核态DPL_KERNEL-0)
        SETGATE(idt[i], 0, GD_KTEXT, __vectors[i], DPL_KERNEL);
    }
	// set for switch from user to kernel
    // 用户态与内核态的互相转化是通过中断实现的，单独为其一个中断描述符
    // 由于需要允许用户态的程序访问使用该中断，DPL特权级为用户态DPL_USER-3
    SETGATE(idt[T_SWITCH_TOK], 0, GD_KTEXT, __vectors[T_SWITCH_TOK], DPL_USER);
	// load the IDT 令IDTR中断描述符表寄存器指向idt_pd，加载IDT
    // idt_pd结构体中的前16位为描述符表的界限，pd_base指向之前完成了赋值操作的idt数组的起始位置
    lidt(&idt_pd);
}
bool trap_in_kernel(struct trapframe *tf) {
    return (tf->tf_cs == (uint16_t)((((1) << 3)) | (0)));
}
void print_trapframe(struct trapframe *tf) {
    printf("trapframe at %p\n", tf);
    print_regs(&tf->tf_regs);
    printf("  ds   0x----%04x\n", tf->tf_ds);
    printf("  es   0x----%04x\n", tf->tf_es);
    printf("  fs   0x----%04x\n", tf->tf_fs);
    printf("  gs   0x----%04x\n", tf->tf_gs);
    printf("  trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
    printf("  err  0x%08x\n", tf->tf_err);
    printf("  eip  0x%08x\n", tf->tf_eip);
    printf("  cs   0x----%04x\n", tf->tf_cs);
    printf("  flag 0x%08x ", tf->tf_eflags);

    int i, j;
    for (i = 0, j = 1; i < sizeof(IA32flags) / sizeof(IA32flags[0]); i ++, j <<= 1) {
        if ((tf->tf_eflags & j) && IA32flags[i] != NULL) {
            printf("%s,", IA32flags[i]);
        }
    }
    printf("IOPL=%d\n", (tf->tf_eflags & 0x00003000) >> 12);

    if (!trap_in_kernel(tf)) {
        printf("  esp  0x%08x\n", tf->tf_esp);
        printf("  ss   0x----%04x\n", tf->tf_ss);
    }
}

void print_regs(struct pushregs *regs) {
    printf("  edi  0x%08x\n", regs->reg_edi);
    printf("  esi  0x%08x\n", regs->reg_esi);
    printf("  ebp  0x%08x\n", regs->reg_ebp);
    printf("  oesp 0x%08x\n", regs->reg_oesp);
    printf("  ebx  0x%08x\n", regs->reg_ebx);
    printf("  edx  0x%08x\n", regs->reg_edx);
    printf("  ecx  0x%08x\n", regs->reg_ecx);
    printf("  eax  0x%08x\n", regs->reg_eax);
}

struct trapframe switchk2u, *switchu2k;

static void trap_dispatch(struct trapframe *tf) {
    char c;
    switch (tf->tf_trapno) {
    case IRQ_OFFSET + IRQ_TIMER:
        ticks ++;
        if (ticks % TICK_NUM == 0) {
            // printf("this is clock trap num id = [ %d ]\n",tf->tf_trapno);
        }
    break;
    case IRQ_OFFSET + IRQ_KBD:
        c = cons_getc();
        if(c){
            //c = 0 key up
            //printf("serial [%03d] %c\n", c, c);
            printf("%c",c);
        }
    break;
    case IRQ_OFFSET + IRQ_COM1:
        c = cons_getc();
        printf("serial [%03d] %c\n", c, c);
    break;
    case T_SWITCH_TOU:
    if (tf->tf_cs != USER_CS) {
        switchk2u = *tf;
        switchk2u.tf_cs = USER_CS;
        switchk2u.tf_ds = switchk2u.tf_es = switchk2u.tf_ss = USER_DS;
        switchk2u.tf_esp = (uint32_t)tf + sizeof(struct trapframe) - 8;

        switchk2u.tf_eflags |= FL_IOPL_MASK;
    
        *((uint32_t *)tf - 1) = (uint32_t)&switchk2u;
    }
    break;
    case T_SWITCH_TOK:
        if (tf->tf_cs != KERNEL_CS) {
            tf->tf_cs = KERNEL_CS;
            tf->tf_ds = tf->tf_es = KERNEL_DS;
            tf->tf_eflags &= ~FL_IOPL_MASK;
            switchu2k = (struct trapframe *)(tf->tf_esp - (sizeof(struct trapframe) - 8));
            memmove(switchu2k, tf, sizeof(struct trapframe) - 8);
            *((uint32_t *)tf - 1) = (uint32_t)switchu2k;
        }
    break;
    default:
    // in kernel, it must be a mistake
    if ((tf->tf_cs & 3) == 0) {
        print_trapframe(tf);
        printf("unexpected trap in kernel num id = [ %d ]\n",tf->tf_trapno);
    }
    }
}
void trap(struct trapframe *tf) {
    // dispatch based on what type of trap occurred
    trap_dispatch(tf);
}